#ifndef _SPI_H_
#define _SPI_H_
#include <String>
#include <String.h>
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <pthread.h>

using namespace std;
///
/// \brief linux spi操作类，含部分MCP2515特有指令的实现
///
class Spi
{
public:
    ///
    /// \brief ＩＯ驱动操作指令
    ///
    enum IO_CMD
    {
     IOCTL_CRTL_HIGHT = 1,      ///< SPI1_CS 拉高
     IOCTL_CRTL_LOW  = 0,       ///< SPI1_CS 置低
     IOCTL_CRTL_MOTOR_POWERON = 3,  ///< 电机驱动器上电
     IOCTL_CRTL_MOTOR_POWEROFF = 5, ///< 电机驱动器电源关闭
     IOCTL_CRTL_SPI2_CS_PIN_HIGH = 7,///< SPI2_CS 拉高
     IOCTL_CRTL_SPI2_CS_PIN_LOW = 9  ///< SPI2_CS 置低
    };
public:
    ///
    /// \brief 构造函数
    /// \param device   设备文件路径
    /// \param speed    设备速度设定
    /// \param bits     位宽设定，默认８
    ///
    Spi(string device,
        u_int32_t speed = 500000,
        u_int8_t bits = 8
        );

    ///
    /// \brief  初始化spi设备
    /// \return >0:初始化成功
    ///
    int Init(void);

    ///
    /// \brief 写入一个字节的数据
    /// \param data
    /// \return >0:操作成功
    ///
    int WriteOneByte(u_int8_t data);

    ///
    /// \brief 按地址写入一个字节数据
    /// \param address 写入地址
    /// \param data
    /// \return
    ///
    int WriteByte(u_int8_t address, u_int8_t data);

    ///
    /// \brief 连续写入数据
    /// \param address  起始地址
    /// \param dataBuf  数据存储指针
    /// \param len  写入长度
    /// \return >0:操作成功
    ///
    int Write(u_int8_t address, u_int8_t* dataBuf, int len);

    ///
    /// \brief 读取一个字节
    /// \param cmd 读取地址
    /// \return >0:操作成功
    ///
    u_int8_t ReadByte(u_int8_t cmd);

    ///
    /// \brief 连续读取数据
    /// \param address  起始地址
    /// \param readBuf  数据存储指针
    /// \param len  读取长度
    /// \return >0:操作成功
    ///
    int Read(u_int8_t address, u_int8_t* readBuf, int len);

    ///
    /// \brief 传输一次数据
    /// \param sendBuf  写入数据存储指针
    /// \param readBuf  读取数据存储指针
    /// \param len  读取长度
    /// \return >0:操作成功
    ///
    int transfer(u_int8_t *sendBuf, u_int8_t *readBuf, int len);

    ///
    /// \brief 掩码写入寄存器,只写入需要操作的寄存器的位
    /// \param RegAddr  寄存器地址
    /// \param mask     掩码，１为写入，０为不写入
    /// \param data     写入的数据
    /// \return >0:操作成功
    ///
    int ModifyRegister(const u_int8_t RegAddr, const u_int8_t mask, const u_int8_t data);

    ///
    /// \brief 快速装载发送寄存器
    /// \param regNum   发送寄存器号
    /// \param buf      写入的数据
    /// \param len      写入长度
    /// \return >0:操作成功
    ///
    int FastWriteTxBuf(const u_int8_t regNum, const u_int8_t* buf, const u_int8_t len);

    ///
    /// \brief 快速读取接收寄存器
    /// \param regNum   寄存器号
    /// \param buf      读取数据存储指针
    /// \return >0:操作成功
    ///
    int FastReadRxBuf(const u_int8_t regNum, u_int8_t *buf);//the size of buf must longer than 13

    ///
    /// \brief 读取当前器件的状态
    /// \return 器件的状态
    ///
    u_int8_t ReadStatus();

    ///
    /// \brief 操作cs引脚
    /// \param flag 操作的指令(详见IO_CMD)
    ///
    void CsPinControl(long flag);

    ///
    /// \brief 获取输入管脚状态
    /// \return
    ///
    u_int8_t GetIntPinStatus();

    ///
    /// \brief 设置中断信号的处理函数
    /// \param handler
    /// \warning 调用Init之前必须先调用此函数
    ///
    ///void SetIrqHandler(boost::function<void (int x)> handler);

    /// spi是否已经链接
    bool isConnected;

private:
    string devicePath;
    u_int32_t speed;
    u_int8_t bits;
    int deviceFd;
    int csPinFd;
    u_int8_t mode;
    u_int8_t delay;
    pthread_mutex_t spiMutex;
};

#endif
